using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Collections;

using SlimDX;

using miciv;

namespace micivCore.Data.UnitTechniques
{
    public class MoveTechnique : UnitTechniqueBase
    {
        protected enum ApplyState
        {
            INIT,
            FLUSHMOUSE,
            WAITSELECTION,
            SELECTION,
        }

        protected string m_strType;
        protected float m_fSpeed;
        protected ApplyState m_state;
        protected MapCoords m_selectedMapCoord;
        protected MouseInput m_mi;
        protected Module m_module;
        protected Hashtable m_htblEligibleMapTiles;
        protected List<MapTile> m_lstRejectedMapTiles;

        public MoveTechnique(CoreContext _coreContext, XmlElement _xmlTechnique)
            : base(_coreContext, _xmlTechnique)
        {
            CoreManager coreManager = this.m_coreContext.CoreManager;
            this.m_strType = _xmlTechnique.Attributes["type"].Value;
            this.m_fSpeed = float.Parse(coreManager.GetXmlAttributeValue(_xmlTechnique, "speed", "1.0"));
            this.m_htblEligibleMapTiles = new Hashtable();
            this.m_lstRejectedMapTiles = new List<MapTile>();
            this.m_state = ApplyState.INIT;
        }

        override public bool Apply(IUnit _unit)
        {
            bool bResult = base.Apply(_unit);
            if (false != bResult)
            {
                switch (this.m_state)
                {
                    case ApplyState.INIT:
                    {
                        this.m_selectedMapCoord = new MapCoords(0, 0);
                        this.m_mi = this.m_coreContext.GetData<MouseInput>("MouseInput");
                        this.m_module = this.m_coreContext.GetData<Module>("micivCore.Module");
                        this.ShowEligibleMapTiles(_unit);
                        this.m_state = ApplyState.FLUSHMOUSE;
                        break;
                    }
                    case ApplyState.FLUSHMOUSE:
                    {
                        if (false != this.m_mi.JustReleasedButtons[0])
                        {
                            this.m_state = ApplyState.WAITSELECTION;
                        }
                        break;
                    }
                    case ApplyState.WAITSELECTION:
                    {
                        MapTile mapTile = _unit.MapTile.MapCommunicator.GetMapTile(this.m_selectedMapCoord);
                        if (null != mapTile)
                        {
                            mapTile.SetVisible("selected", false);
                            mapTile.SetVisible("noteligible", false);
                        }

                        Vector3 f3World = this.m_module.RestrictedRendererContextes[0].Camera.Unproject(this.m_mi.X, this.m_mi.Y);
                        _unit.MapTile.MapCommunicator.ComputeWorldToMapCoord(f3World.X, f3World.Y, out this.m_selectedMapCoord);
                        mapTile = _unit.MapTile.MapCommunicator.GetMapTile(this.m_selectedMapCoord);
                        if (null != mapTile)
                        {
                            if (false != this.m_htblEligibleMapTiles.ContainsKey(mapTile))
                            {
                                mapTile.SetVisible("selected", true);
                                if (false != this.m_mi.JustReleasedButtons[0])
                                {
                                    this.m_state = ApplyState.SELECTION;
                                }
                            }
                            else
                            {
                                mapTile.SetVisible("noteligible", true);
                            }

                        }
                        break;
                    }
                    case ApplyState.SELECTION:
                    {
                        bResult = false;
                        this.ResetApply(_unit);
                        break;
                    }
                }
                // right click for cancel
                if (false != this.m_mi.JustReleasedButtons[1])
                {
                    bResult = false;
                    this.ResetApply(_unit);
                }
            }
            return bResult;
        }

        protected void ResetApply(IUnit _unit)
        {
            this.m_state = ApplyState.INIT;
            foreach (DictionaryEntry de in this.m_htblEligibleMapTiles)
            {
                (de.Key as MapTile).SetVisible("eligible", false);
            }
            this.m_htblEligibleMapTiles.Clear();
            this.m_lstRejectedMapTiles.Clear();
            MapTile mapTile = _unit.MapTile.MapCommunicator.GetMapTile(this.m_selectedMapCoord);
            mapTile.SetVisible("selected", false);
            mapTile.SetVisible("noteligible", false);
        }

        protected void ShowEligibleMapTiles(IUnit _unit)
        {
            this.TraverseMap(this.m_fSpeed, _unit.MapTile);
            foreach (DictionaryEntry de in this.m_htblEligibleMapTiles)
            {
                (de.Key as MapTile).SetVisible("eligible", true);
            }
        }

        protected void TraverseMap(float _fSpeed, MapTile _mapTile)
        {
            List<MapTile> lstNeighbors = new List<MapTile>(6);
            _mapTile.GetNeighbors(ref lstNeighbors);
            foreach (MapTile neighbor in lstNeighbors)
            {
                // positive value means that neighbor has compatible layer type
                float fSpeedToNeighbor = _mapTile.GetSpeed(this.m_strType);
                // positive or null value means that unit can move to neighbor
                float fMoveToNeighbor = _fSpeed - fSpeedToNeighbor;

                if (0.0f < fSpeedToNeighbor)
                {
                    if (0.0f <= fMoveToNeighbor)
                    {
                        if (false != this.m_htblEligibleMapTiles.ContainsKey(neighbor))
                        {
                            // if neighbor is already eligible then check is computed move is better than previously stored one.
                            if (fMoveToNeighbor < (float)this.m_htblEligibleMapTiles[neighbor])
                            {
                                this.m_htblEligibleMapTiles[neighbor] = fMoveToNeighbor;
                                this.TraverseMap(fMoveToNeighbor, neighbor);
                            }
                        }
                        else
                        {
                            this.m_htblEligibleMapTiles.Add(neighbor, fMoveToNeighbor);
                            this.TraverseMap(fMoveToNeighbor, neighbor);
                        }
                    }
                }
                else if (false == this.m_lstRejectedMapTiles.Contains(neighbor))
                {
                    this.m_lstRejectedMapTiles.Add(neighbor);
                }
            }
        }
    }
}
